#include "can.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

// #define test

volatile int8_t read_data_flag;

/*! @brief FlexCAN message buffer CODE for Rx buffers. */
enum _flexcan_mb_code_rx {
  kFLEXCAN_RxMbInactive = 0x0, /*!< MB is not active.*/
  kFLEXCAN_RxMbFull = 0x2,     /*!< MB is full.*/
  kFLEXCAN_RxMbEmpty = 0x4,    /*!< MB is active and empty.*/
  kFLEXCAN_RxMbOverrun = 0x6,  /*!< MB is overwritten into a full buffer.*/
  kFLEXCAN_RxMbBusy = 0x8, /*!< FlexCAN is updating the contents of the MB.*/
                           /*!  The CPU must not access the MB.*/
  kFLEXCAN_RxMbRanswer =
      0xA, /*!< A frame was configured to recognize a Remote Request Frame */
           /*!  and transmit a Response Frame in return.*/
  kFLEXCAN_RxMbNotUsed = 0xF, /*!< Not used.*/
};

/*! @brief FlexCAN message buffer CODE FOR Tx buffers. */
enum _flexcan_mb_code_tx {
  kFLEXCAN_TxMbInactive = 0x8, /*!< MB is not active.*/
  kFLEXCAN_TxMbAbort = 0x9,    /*!< MB is aborted.*/
  kFLEXCAN_TxMbDataOrRemote =
      0xC, /*!< MB is a TX Data Frame(when MB RTR = 0) or */
           /*!< MB is a TX Remote Request Frame (when MB RTR = 1).*/
  kFLEXCAN_TxMbTanswer = 0xE, /*!< MB is a TX Response Request Frame from */
                              /*!  an incoming Remote Request Frame.*/
  kFLEXCAN_TxMbNotUsed = 0xF, /*!< Not used.*/
};

/**
 * @brief 冻结模式开启或关闭
 *
 * @param base CAN_Type
 * @param judge true 开启 |  false 关闭
 */
inline static void can_start_freeze(CAN_Type *base, bool judge) {
  if (judge) {
    base->MCR |= ((0x1 << 30) | (0x1 << 28));
  } else {
    base->MCR &= ~((0x1 << 30) | (0x1 << 28));
  }
}

/**
 * @brief can gpio 初始化
 *
 */
void can_gpio_init(void) {
  IOMUXC_SetPinMux(IOMUXC_UART3_CTS_B_FLEXCAN1_TX, 1);
  IOMUXC_SetPinMux(IOMUXC_UART3_RTS_B_FLEXCAN1_RX, 1);
  gpio_pin_config_t config;
  config.direction = kGPIO_DigitalOutput;
  config.outputLogic = 1;
  config.interruptMode = kGPIO_NoIntmode;
  // TX gpio1_26
  // RX gpio1_27
  gpio_init(GPIO1, 27, &config);
  gpio_init(GPIO1, 26, &config);
}

/**
 * @brief 控制器初始化
 *
 * @param base CAN_TypeCAN_Type
 */
void can_controller_init(CAN_Type *base) {

  // 使能
  base->MCR &= ~(0x1 << 31);

  // 软件复位
  base->MCR |= (0x1 << 25);
  while ((base->MCR & (0x1 << 25))) {
  }

  // 打开冻结模式
  can_start_freeze(base, true);

#if defined(test)
  // 回环测试
  base->MCR &= ~(0x1 << 17);
  base->CTRL1 |= (0x1 << 12);
#endif

  // MAXMB
  base->MCR &= ~(0x7f);
  base->MCR |= (0x3f);
  can_start_freeze(base, false);
}

/**
 * @brief 波特率和采样点设置
 *
 * @param base
 */
void can_protocol_timing_init(CAN_Type *base) {
  can_start_freeze(base, true);

  base->CTRL1 &= ~(0xffff << 16);
  // 分频数
  base->CTRL1 |=
      (0x5 << 24) | (0x0 << 22) | (0x6 << 19) | (0x4 << 16) | (0x6 << 0);

  can_start_freeze(base, false);
}

/**
 * @brief can 发邮箱的配置 Message Buffer
 *
 * @param base
 * @param mb_index
 * @param mb_params
 */
void can_tx_mb_config(CAN_Type *base, uint8_t mb_index,
                      mb_params_t *mb_params) {

  // 未激活 设置code
  base->MB[mb_index].CS |= (kFLEXCAN_TxMbInactive << 24);

  // RTR 0 数据帧
  // IDE 0 标准帧
  base->MB[mb_index].CS &= ~(0x3 << 20);

  // DLC
  base->MB[mb_index].CS &= ~(0xf << 16);
  base->MB[mb_index].CS |= (mb_params->dlc << 16);

  // 给MB指定一个唯一的ID
  base->MB[mb_index].ID &= ~(0x7ff << 18);
  base->MB[mb_index].ID |= (mb_params->frame_id << 18);

  // WORD0 WORD1
  base->MB[mb_index].WORD0 = mb_params->word0;
  base->MB[mb_index].WORD1 = mb_params->word1;

  // 数据帧激活 设置code
  base->MB[mb_index].CS |= (kFLEXCAN_TxMbDataOrRemote << 24);
}

/**
 * @brief 收邮箱的配置
 *
 * @param base
 * @param mb_index
 * @param frame_id
 */
void can_rx_mb_config(CAN_Type *base, uint8_t mb_index, int32_t frame_id) {

  base->MB[mb_index].CS = 0;

  // 未激活code
  base->MB[mb_index].CS |= (kFLEXCAN_RxMbInactive << 24);

  // RTR 0 数据帧
  // IDE 0 标准帧
  base->MB[mb_index].CS &= ~(0x3 << 20);

  // id
  base->MB[mb_index].ID = frame_id << 18;

  // 数据清零
  base->MB[mb_index].WORD0 = 0;
  base->MB[mb_index].WORD1 = 0;

  // 激活 code
  base->MB[mb_index].CS |= (kFLEXCAN_RxMbEmpty << 24);
}

/**
 * @brief 发送数据
 *
 * @param base
 * @param can_frame
 */
void can_send_data(CAN_Type *base, can_frame_t *can_frame) {

  mb_params_t params;
  params.frame_id = can_frame->frame_id;
  params.dlc = can_frame->size;

  uint32_t word0 = 0;
  uint32_t word1 = 0;
  uint8_t i = 0;
  if (can_frame->size > 4) {
    for (i = 0; i < 4; i++) {
      word0 |= (can_frame->buf[i] << (24 - i * 8));
    }
    for (i = 0; i < can_frame->size - 4; i++) {
      word1 |= (can_frame->buf[i + 4] << (24 - i * 8));
    }
  } else {
    for (i = 0; i < can_frame->size; i++) {
      word0 |= (can_frame->buf[i] << (24 - i * 8));
    }
  }
  params.word0 = word0;
  params.word1 = word1;
  can_tx_mb_config(base, can_frame->index, &params);

  // 判断发送是否完成
  while (!(base->IFLAG1 & (0x1 << can_frame->index)))
    ;
  base->IFLAG1 |= (0x1 << can_frame->index);
}

/**
 * @brief 接收数据
 *
 * @param base
 * @param can_frame
 * @return uint8_t  0 失败  | 1 成功
 */
uint8_t can_read_data(CAN_Type *base, can_frame_t *can_frame) {

  while (!(base->IFLAG1 & (0x1 << can_frame->index)))
    ;
  uint8_t code = 0;
  code = ((base->MB[can_frame->index].CS >> 24) & 0xf);
  if (code != kFLEXCAN_RxMbFull) {
    return 0;
  }

  uint8_t res_size = 0;
  res_size = ((base->MB[can_frame->index].CS >> 16) & 0xf);

  if (res_size > can_frame->size) {
    return 0;
  }

  uint8_t i = 0;
  uint32_t word0 = 0;
  uint32_t word1 = 0;
  if (res_size > 4) {
    word0 = base->MB[can_frame->index].WORD0;
    word1 = base->MB[can_frame->index].WORD1;
    for (i = 0; i < 4; i++) {
      can_frame->buf[i] = ((word0 >> 24) & 0xff);
      word0 <<= 8;
    }
    for (i = 0; i < res_size - 4; i++) {
      can_frame->buf[i + 4] = ((word1 >> 24) & 0xff);
      word1 <<= 8;
    }
  } else {
    word0 = base->MB[can_frame->index].WORD0;
    for (i = 0; i < res_size; i++) {
      can_frame->buf[i] = ((word0 >> 24) & 0xff);
      word0 <<= 8;
    }
  }
  can_frame->size = res_size;

  // 配置
  can_rx_mb_config(base, can_frame->index, can_frame->frame_id);
  // 清理中断标志
  base->IFLAG1 |= (0x1 << can_frame->index);
  return 1;
}

/**
 * @brief           中断处理函数
 *
 * @param giccIar
 * @param param
 */
void can_irq_handler_t(unsigned int giccIar, void *param) {

  if (read_data_flag == 0) {
    if (can_read_data(CAN1, ((can_frame_t *)param))) {
      read_data_flag = 1;
    }
  }
}

/**
 * @brief
 *
 * @param base
 * @param mb_index
 */
void can_interrupt_init(CAN_Type *base, can_frame_t *can_frame) {

  if (can_frame->index < 32) {
    base->IMASK1 |= (0x1 << can_frame->index);
  } else {
    base->IMASK2 |= (0x1 << (can_frame->index - 32));
  }

  system_register_irqhandler(CAN1_IRQn, can_irq_handler_t, can_frame);
  GIC_EnableIRQ(CAN1_IRQn);
}
